package com.yonyougov.devops.cruxcisupport.appenvport;

import com.yonyougov.devops.cruxcisupport.app.AppDao;
import com.yonyougov.devops.cruxcisupport.app.AppEntity;
import com.yonyougov.devops.cruxcisupport.app.AppService;
import com.yonyougov.devops.cruxcisupport.appenv.AppEnvDao;
import com.yonyougov.devops.cruxcisupport.appenv.AppEnvEntity;
import com.yonyougov.devops.cruxcisupport.appenv.AppEnvPortDao;
import com.yonyougov.devops.cruxcisupport.appenv.AppEnvService;
import com.yonyougov.devops.cruxcisupport.appenv.vo.port.instance.*;
import com.yonyougov.devops.cruxcisupport.appport.AppPortDao;
import com.yonyougov.devops.cruxcisupport.appport.AppPortEntity;
import com.yonyougov.devops.cruxcisupport.env.EnvDao;
import com.yonyougov.devops.cruxcisupport.env.EnvEntity;
import com.yonyougov.devops.cruxcisupport.env.EnvService;
import com.yonyougov.devops.cruxcisupport.envserver.EnvServerDao;
import com.yonyougov.devops.cruxcisupport.envserver.vo.EnvServerPageLikeReqVoParams;
import com.yonyougov.devops.cruxcisupport.util.BusinessUtils;
import com.yonyougov.devops.cruxcisupport.util.CruxDevOpsIdGenerator;
import com.yonyougov.devops.fwc.core.dao.BaseDao;
import com.yonyougov.devops.fwc.core.entity.Pagination;
import com.yonyougov.devops.fwc.core.service.impl.BaseServiceImpl;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.beans.Transient;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * t_app_env_port服务实现
 */
@Slf4j
@Service
public class AppEnvPortServiceImpl extends BaseServiceImpl<AppEnvPortEntity, BigInteger> implements AppEnvPortService {
    private final AppEnvPortDao appEnvPortDao;
    private final AppDao appDao;
    private final EnvDao envDao;
    private final AppEnvDao appEnvDao;
    private final AppPortDao appPortDao;
    private final EnvServerDao envServerDao;
    @Autowired
    private AppEnvService appEnvService;
    @Autowired
    private EnvService envService;
    @Autowired
    private AppService appService;



    public AppEnvPortServiceImpl(AppEnvPortDao appEnvPortDao, AppDao appDao, EnvDao envDao, AppEnvDao appEnvDao, AppPortDao appPortDao, EnvServerDao envServerDao) {
        this.appEnvPortDao = appEnvPortDao;
        this.appDao = appDao;
        this.envDao = envDao;
        this.appEnvDao = appEnvDao;
        this.appPortDao = appPortDao;
        this.envServerDao = envServerDao;
    }

    @Override
    public BaseDao<AppEnvPortEntity, BigInteger> getDao() {
        return appEnvPortDao;
    }

    //<!--用户自定义代码开始-->
    @Override
    public List<Integer> getMappingPort(String envName, String appName) {
        //获取环境Id
        EnvEntity envRequestEntity = new EnvEntity();
        envRequestEntity.setName(envName);
        String portRange = envDao.findBy(envRequestEntity).getPortRange();
        //获取应用Id
        AppEntity appRequestEntity = new AppEntity();
        appRequestEntity.setApp(appName);
        AppEntity appEntity = appDao.findBy(appRequestEntity);
        if (appEntity.getExposePort()) {
            //获取appPort实体类
            AppPortEntity appPortEntityRequest = new AppPortEntity();
            appPortEntityRequest.setAppId(appEntity.getId());
            // 这里无需判断，因为项目启动之前k8s的部署文件已经生成，映射端口表中必有值
            return appPortDao.findAllBy(appPortEntityRequest).stream().map(appPortEntity -> {
                return BusinessUtils.generateMappingPort(portRange, appEntity.getSerialNumber(), appPortEntity.getIdx());
            }).sorted().collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    @SneakyThrows
    @Override
    public void generateMappingPort(BigInteger envId) {
        //获取环境信息
        EnvEntity envEntity = envDao.findByPrimaryKey(envId);
        String portRange = envEntity.getPortRange();
        //获取部署信息
        AppEnvEntity appEnvRequestEntity = new AppEnvEntity();
        appEnvRequestEntity.setEnvId(envId);
        List<AppEnvEntity> appEnvEntityList = appEnvDao.findAllBy(appEnvRequestEntity);
        //判断当前环境是否需要对外暴露端口
        if (null != envEntity.getExposePort() && envEntity.getExposePort()) {
            for (AppEnvEntity appEnvEntity : appEnvEntityList) {
                AppEntity appEntity = appDao.findByPrimaryKey(appEnvEntity.getAppId());
                handleAppEnvPort(appEnvEntity.getId(), appEntity, portRange,true);
            }
        } else {
            //不需要暴露端口，需要对需要暴露的应用添加
            for (AppEnvEntity appEnvEntity : appEnvEntityList) {
                AppEntity appEntity = appDao.findByPrimaryKey(appEnvEntity.getAppId());
                if (null != appEntity.getExposePort() && appEntity.getExposePort()) {
                    handleAppEnvPort(appEnvEntity.getId(), appEntity, portRange,true);
                }
            }
        }
    }

    @SneakyThrows
    @Transactional
    @Override
    public EnvAppServerInfoVo getEnvAppServerInfo(String envName, String appName) {
        EnvEntity envEntityRequest = new EnvEntity();
        envEntityRequest.setName(envName);
        EnvEntity envEntity = envDao.findBy(envEntityRequest);
        if (null == envEntity) {
            throw new Exception("环境名称不存在");
        }
        BigInteger envId = envEntity.getId();
        AppEntity appEntityRequest = new AppEntity();
        appEntityRequest.setApp(appName);
        AppEntity appEntity = appDao.findBy(appEntityRequest);
        if (null == appEntity) {
            throw new Exception("应用名称不存在");
        }
        BigInteger appId = appEntity.getId();
        AppEnvEntity appEnvEntityRequest = new AppEnvEntity();
        appEnvEntityRequest.setEnvId(envId);
        appEnvEntityRequest.setAppId(appId);
        AppEnvEntity appEnvEntity = appEnvDao.findBy(appEnvEntityRequest);
        if (null == appEnvEntity) {
            throw new Exception("部署信息不存在");
        }
        BigInteger appEnvId = appEnvEntity.getId();
        //获取映射端口信息
        AppEnvPortEntity appEnvPortEntityRequest = new AppEnvPortEntity();
        appEnvPortEntityRequest.setAppEnvId(appEnvId);
        List<AppEnvPortEntity> AppEnvPortEntityList = appEnvPortDao.findAllBy(appEnvPortEntityRequest);
        List<AppPortsVo> appPortsVoList = AppEnvPortEntityList.stream().map(appEnvPortEntity -> {
            AppPortsVo appPortsVo = new AppPortsVo();
            appPortsVo.setName(appEnvPortEntity.getName());
            appPortsVo.setPort(appEnvPortEntity.getPort());
            appPortsVo.setOutPort(appEnvPortEntity.getMappingPort());
            return appPortsVo;
        }).collect(Collectors.toList());
        InstanceVo instanceVo = new InstanceVo();
        instanceVo.setPorts(appPortsVoList);
        instanceVo.setExposePort(appEntityRequest.getExposePort() != null ? appEntityRequest.getExposePort() : false);
        //获取服务器信息
        EnvServerPageLikeReqVoParams envServerPageLikeReqVoParams = new EnvServerPageLikeReqVoParams();
        envServerPageLikeReqVoParams.setEnvId(envId);
        envServerPageLikeReqVoParams.setPageAble(false);
        List<EnvNodeVo> envNodeVoList = envServerDao.findAllByParamsLike(envServerPageLikeReqVoParams).stream().map(envServer -> {
            EnvNodeVo envNodeVo = new EnvNodeVo();
            envNodeVo.setIp(envServer.getIp());
            envNodeVo.setMaster(envServer.getMaster());
            return envNodeVo;
        }).collect(Collectors.toList());
        EnvClusterVo envClusterVo = new EnvClusterVo();
        envClusterVo.setNodes(envNodeVoList);
        //封装结果集
        EnvAppServerInfoVo envAppServerInfoVo = new EnvAppServerInfoVo();
        envAppServerInfoVo.setInstance(instanceVo);
        envAppServerInfoVo.setCluster(envClusterVo);
        envAppServerInfoVo.setMasterIp(envEntity.getMasterIp());
        envAppServerInfoVo.setExposePort(envEntity.getExposePort() != null ? envEntity.getExposePort() : false);
        return envAppServerInfoVo;
    }


    /**
     * 如何之前已经存在记录 删除掉重新创建
     *
     * @param appEnvId  部署id
     * @param appEntity 应用实体类
     * @param portRange 环境端口范围
     * @param isForce true: 强制生成，不校验端口是否已经被别的服务占用，false: 生成之前检查端口是否被别的服务占用，如果占用则拒绝生成
     */
    private void handleAppEnvPort(BigInteger appEnvId, AppEntity appEntity, String portRange,Boolean isForce) throws Exception{
        AppEnvPortEntity appEnvPortEntityRequest = new AppEnvPortEntity();
        appEnvPortEntityRequest.setAppEnvId(appEnvId);
        List<AppEnvPortEntity> appEnvPortEntities = appEnvPortDao.findAllBy(appEnvPortEntityRequest);
        if (null != appEnvPortEntities && appEnvPortEntities.size() > 0) {
            //删除之前的
            appEnvPortEntities.forEach(appEnvPortEntity -> appEnvPortDao.deleteByPrimaryKey(appEnvPortEntity));
        }
        String serialNumber = appEntity.getSerialNumber();
        AppPortEntity appPortRequest = new AppPortEntity();
        appPortRequest.setAppId(appEntity.getId());
        List<AppPortEntity> appPortEntityList = appPortDao.findAllBy(appPortRequest);
        for (AppPortEntity appPortEntity : appPortEntityList) {
            Integer idx = appPortEntity.getIdx();
            AppEnvPortEntity appEnvPortEntity = new AppEnvPortEntity();
            appEnvPortEntity.setId(new CruxDevOpsIdGenerator().generatorId(appEnvPortDao));
            appEnvPortEntity.setAppEnvId(appEnvId);
            appEnvPortEntity.setName(appPortEntity.getName());
            appEnvPortEntity.setPort(appPortEntity.getInternalPort());
            Integer mappingPort = BusinessUtils.generateMappingPort(portRange, serialNumber, idx);
            if(!isForce){
                AppEnvEntity appEnvEntity = appEnvService.findBy(AppEnvEntity.builder().id(appEnvId).build());
                List<AppEntity> apps = appEnvPortDao.findAppByMappingPortInEnv(mappingPort,appEnvEntity.getEnvId());
                if(apps.size() > 0){
                    throw new Exception(String.format("即将生成的端口为%s,但端口%s已经被服务%s占用",mappingPort,mappingPort,apps.get(0).getApp()));
                }
            }
            appEnvPortEntity.setMappingPort(mappingPort);
            appEnvPortDao.insert(appEnvPortEntity);
        }
    }

    @Override
    public void batchDeleteById(List<BigInteger> ids) {
        appEnvPortDao.batchDelete(ids);
    }

    @SneakyThrows
    @Override
    public void generateAppMappingPort(BigInteger appEnvId){
        //获取部署信息
        AppEnvEntity appEnvEntity = appEnvService.findBy(AppEnvEntity.builder().id(appEnvId).build());

        //获取环境信息
        EnvEntity envEntity = envService.findBy(EnvEntity.builder().id(appEnvEntity.getEnvId()).build());
        String portRange = envEntity.getPortRange();

        // 获取应用信息
        AppEntity appEntity = appService.findBy(AppEntity.builder().id(appEnvEntity.getAppId()).build());

        handleAppEnvPort(appEnvEntity.getId(), appEntity, portRange,false);
    }
    //<!--用户自定义代码结束-->
}
